#include "WOPM.h"
#include "Command_Agent.h"
#include "animationCenter.h"
#include <ctime>
#include "DIVA_Application.h"

WOPM* WOPM::_wop = NULL;
WOPM::WOPM(){	
}
WOPM* WOPM::getInstance(){
	if(_wop==NULL){
		_wop = new WOPM();
	}
	return _wop;
}
WOPM::~WOPM(){
}

WOP_Entry* WOPM::createEntry(string name,string wop_type, string vartype,string addr){
	assert(name.length() > 0);
	if (name.length() == 0)
		return NULL;

	WOP_Entry *en;
	en = createEntry(name,wop_type,NULL,NULL, false, vartype,addr);
	return en;

}
void WOPM::revive_sleepwop(WOP_Entry *en, string name, WOP_Entry* from){
     en->setName(name) ;  // change the name (the path that is used to ask Minerva) is the most important thing
	 en->setFrom(from);
	 en->setDescentFrom(from);
	 en->setScope(true);
}
WOP_Entry* WOPM::createEntry(string name,string type, WOP_Entry *parent, WOP_Entry* from, bool top, string vartype, string addr){
	assert(name.length() > 0);
	WOP_Entry* entry = NULL;	
	if (name.length() == 0)
		return NULL;

	assert(vartype != "");
	/*改成type.find - by Ming Jie Chiou*/
	if(type.find("long") != type.npos){
		entry = new WOP_Int_Proxy(new WOP_Int_Entry(name,parent));//modify by hyt , 09/10/05
	}
	if(type.find("int") != type.npos){
		entry = new WOP_Int_Proxy(new WOP_Int_Entry(name,parent));//modify by hyt , 09/10/05							
	}

	if(type.find("bool") != type.npos){
		entry = new WOP_Bool_Proxy(new WOP_Bool_Entry(name,parent));//modify by hyt , 09/10/05							
		
	}
	if(type.find("char") != type.npos){
		entry = new WOP_Char_Proxy(new WOP_Char_Entry(name,parent));//modify by hyt , 09/10/05							
		
	}
	if(type.find("double") != type.npos){
		entry = new WOP_Double_Proxy(new WOP_Double_Entry(name,parent));//modify by hyt , 09/10/05							
		
	}
	if(type.find("float") != type.npos){
		entry = new WOP_Float_Proxy(new WOP_Float_Entry(name,parent));//modify by hyt , 09/10/05							
		
	}
	if(type.find("String") != type.npos){
		entry = new WOP_String_Proxy(new WOP_String_Entry(name,parent));//modify by hyt , 09/10/05									
	}

	if(type.find("array") != type.npos){
		entry = new WOP_ArrayRef_Entry(name,parent);							
		
	}
	if(type.find("ref") != type.npos){
		entry = new WOP_Ref_Entry(name,parent);
		
	}
	assert(entry != NULL);
	entry->update();
	entry->setAddr(addr);
	entry->setVarType(vartype);
	entry->setFrom(from);
	entry->setDescentFrom(from);
	entry->setTop(top);

	return entry;
				
}
void WOPM::addEntry(WOP_Entry *entry){
	assert(entry != NULL);

	table.push_back(entry);
	/*
	//add前總要有點過濾條件...暫定為不允許任何物件有重複的key+parent
	//不過，我更贊同取代掉舊的資料。因為user手動要求觀看的資料，型態隨時會變的。
	if (search(entry->getName(),entry->getParent())==NULL){
		//_table->add(entry);
		table.push_back(entry);
		return;
	}
	//but，那些被淘汰的物件可能面臨memory leaking的問題。所以我直接在這給他們了斷...
	delete entry;
	*/
}

void WOPM::removeEntry(WOP_Entry *entry){
	assert(entry != NULL);
	if(entry == NULL)
		return;

	std::vector<WOP_Entry*>::iterator itr;

	for (itr = this->table.begin(); itr != this->table.end();itr++)
	{
		if (entry == (*itr))
		{
			this->table.erase(itr);
			break;
		}
	}

}

WOP_Entry* WOPM::search(string key,WOP_Entry* parent){ // given a string+parent and search
	assert(false); // ALERT ! NO LONGER USED
	return NULL ;
	/*
	assert(key.size()>0);
	WOP_Entry *en;
	WOP_Entry *enP = NULL;
	WOP_Entry *enC = NULL;

	std::vector<std::string> splitMsg;
	std::vector<std::string>::iterator itr;
	std::vector<WOP_Entry*>::iterator tableItr;

	/// 將所有會接收得到的字串格式列出，雖然很醜，但若是日後又有新的字串格式進來，
	/// 只要不更動以DOT為切割的key的大前提下，將新進字串格式的判斷加在第一個if的條件判斷即可
	/// format of name, name@location, instance of type[]
	for(tableItr=this->table.begin();tableItr!=this->table.end();tableItr++){
		en = (*tableItr);
		if ((en->getParent() == parent)&&(en->getName().compare(key) == 0))
				return en;
	}

	if ((key.find(FLAG_AT) != key.npos) || (key.find(BLANK) != key.npos) || ((key.find(FLAG_AT) == key.npos) && (key.find(BLANK) == key.npos) && (key.find(DOT) == key.npos) && (key.find('[') == key.npos) ) )   
	{
		for (tableItr = this->table.begin();tableItr != this->table.end();tableItr++)
		{
			en = (*tableItr);
			if ((en->getParent() == parent)&&(en->getName().compare(key) == 0))
				return en;
		}
		return NULL;
	}	
	else /// format of name[], name1.name2, name1[].name2, name1.name2[], 
	{
		/// 先在"["之前加上DOT，接下來split才能順利進行，必須從index1開始，否則會出現index的error
		for(unsigned long i = 1;i < key.length(); i++)
		{
			assert(i-1 >= 0);
			if (((key.at(i)) == '[')&&((key.at(i-1))!=']'))
			{
				key.insert(i,DOT);
				i++; // insert了DOT，則必須讓index後退一格，否則會infinite loop
			}
		}

		/// 以DOT作為split 的key。
		splitMsg = Command_Agent::getInstance()->stringSplit(key,DOT);
		
		if ((splitMsg.begin()+1) == splitMsg.end())
		{
			for (tableItr = this->table.begin();tableItr != this->table.end();tableItr++)
			{
				en = (*tableItr);
				if ((en->getParent() == parent)&&(en->getName().compare(key) == 0))
					return en;
			}
			return NULL;
		}

		/// 開始進行search
		for (itr = splitMsg.begin(); itr != splitMsg.end(); itr++)
		{
			if ((enC = search(*itr,enP))!=NULL)
			{
				enP = search(enC->getVal());
			}
			else 
			{
				break;
			}
		}
		if (enC != NULL)
			return enC;

		return NULL;
	}
	*/
}

WOP_Entry* WOPM::search(string key){ //equal to search(key,NULL);
	assert(false); // ALERT ! NO LONGER USED
	/*
	assert(key.size()>0);
	WOP_Entry *en;
	en = search(key,NULL);
	*/
	return NULL;
}

void WOPM::unfold(WOP_Entry *entry){ // unfolding the entry
	assert(entry != NULL);
	entry->unfold();//只有RefEntry有用
}
void WOPM::updateDescents(WOP_Entry *refen) {
	WOP_Entry *en;
	vector<WOP_Entry*> ens ;
	std::vector<WOP_Entry*>::iterator itr;
	for (itr = this->table.begin();itr != this->table.end();itr++)
	{	
		en = (*itr);
		ens.push_back(en);
	}
	// why we do this because en->update may change the table
	std::vector<WOP_Entry*>::iterator enitr;
	for (enitr = ens.begin(); enitr != ens.end() ; enitr++) {
		en = (*enitr);
		if (en->getFrom() == refen) {
			// so the previous refen still point to this en
			Command_Agent::getInstance()->	unfoldRefPure(refen);
			// we make sure the name is updated using refen's name as prefix
			en->setScope(true);
			if (en->isUpdated() == false) 
				   en->update();
			if (en->getTypeStr() == "ref" && en->isChanged() == false) {
				//  this wop is a reference WOP and the value has not changed 
				//  so, the path name of its descents still valid. In this case, we 
				// continue updating the descents.
				updateDescents(en); // we recursively call the descents to check if it should be update or not
			}  else {
				int i = 0 ;
			}
		}
	}
		/*
		if (en->getDescentFrom() == refen) {
			en->setScope(true);
			if (en->isUpdated() == false) 
				en->update();
		}
		*/

}
vector<WOP_Entry*> WOPM::queryWOPsbyInscope(bool inscope) {
	vector<WOP_Entry*>  results ;		
	WOP_Entry *en;
	std::vector<WOP_Entry*>::iterator itr;
	for (itr = this->table.begin();itr != this->table.end();itr++) {
     	en = (*itr) ;
		if (en->getScope() == inscope) 
			results.push_back(en);
	}
	return results ;

}
void WOPM::updateAll(){
	WOP_Entry *en;
	std::vector<WOP_Entry*>::iterator itr;
	for (itr = this->table.begin();itr != this->table.end();itr++)
	{
		(*itr)->setUpdated(false);
		(*itr)->setChanged(false);
		(*itr)->setScope(false);  // set all the scope to be false
	}

	// 1. we check if the wop visualized by users are still in scope or not.
	for (itr = this->table.begin();itr != this->table.end();itr++) {
		en = (*itr);
		if (en->isTop()) 
		        en->check_scope() ;
	}


	// 2008/5/22 modified by YPC 
	// first we update all the wop entries that has top == true.
	// these wops entries are visualized by user
	vector<WOP_Entry*> ens ;
	for (itr = this->table.begin();itr != this->table.end();itr++)
	{	
		en = (*itr);
		ens.push_back(en);
	}
	// story: before version 624, this is only a for loop
	// however, because of the bug #193, inside the loop, new entries of 
	// table is added which cause STL to crash

	//this->showAll("Check Update 1");
	vector<WOP_Entry*>::iterator enitr ;
	for (enitr = ens.begin(); enitr != ens.end() ; enitr++) {
		en = (*enitr);
		if (en->getScope() == false) 
			continue ; // if the variable is not in scope, skip
		if (en->isTop() && en->isUpdated() == false) {
			en->update();
			// Now the top variables are updated.
			// There are many wops which may be unfolded from these variables (particularly a reference wop)
			// Suppose a top variable X is a reference, and its value does not changed during update
			// We want to recursively update these wops because the path name X.left.right should remain correctly
			// but its content may change
			if (en->getTypeStr() == "ref" && en->isChanged() == false ) {
				updateDescents(en);
			} 
		}
	
	}
	

	//this->showAll("Check Update 2");		

	// After the first step, some value of the pointer, reference is changed.
	// If the value is different from before, en._updated is set to be true ;
	// in general,
	//     if a wop entry is a primitive type, no more actions need to be done
	//     if a wop entry is an array, update() of WOP_heap_Entry is called  recursively to all of its children.
	//     if a wop entry is an object, update() of WOP_Heap_Entry is called recursively to all of its children
	//     if a wop_entry is a reference or pointer, WOP_Ref_Entry's update will take care of the change
	// 
	// In the end, dangling objects will be left and not updated and not changed.
	//if(animationCenter::getInstance()->getStatus()){
	//	animationCenter::getInstance()->computeInterpolationBetweenLastTwoKeyFrame();//add by hyt , 09/10/05
	//}
	//this->showAll("Check Update 2");
}

// given a string, we search if there is any wop_entry whose address
// is the same as the given entry

WOP_Entry* WOPM::check_existing_objects(std::string _value){
	vector<WOP_Entry*> result ;
	WOP_Entry* en = NULL;


	std::vector<WOP_Entry*>::iterator itr;
	int j = table.size();
	for (itr = table.begin(); itr != table.end(); itr++){
		if ((*itr)->getAddr() == "none") 
			continue ;
		if ((*itr)->getAddr() == _value)    
			result.push_back((*itr)) ;
		 
	}
	if (result.size()>=1) { // there are more than one wop entry have the same address 
		                   // it occurs in cool2d, cool2d[0][0] and cool2d[0][0].x
						   // currently we return the top one, the top one should always
		                   // be found first.
		 return result[0] ;
	} else
		 return NULL ;
}

WOP_Entry* WOPM::searchEntrybyName(string name, string type) {

	std::vector<WOP_Entry*>::iterator itr;

	for (itr = table.begin(); itr != table.end(); itr++){
		if ((*itr)->getLongName() == name && (*itr)->getTypeStr() == "ref")
			return (*itr);
	}
	return NULL ;
}
void WOPM::showAll(string title = ""){
	    // test用的函式
		 struct tm t;
         memset(&t, 0, sizeof(struct tm));

		ofstream outfile("testcase.txt", ios_base::out | ios_base::app );

		WOP_Entry *en;
		std::vector<WOP_Entry*>::iterator itr;

		if (title != "") 
			outfile << "(*" << title  <<  "    "   <<  t.tm_hour << ":" << t.tm_min << "*)" << endl ;
		
		/// 輸出到檔案testcase中
		outfile << setw(50) << " WOP Name" << setw(8) << "inscope" <<setw(7) << "TYPE" << setw(20) << "Value" << setw(10) << "Parent" << setw(10) << "Self" << setw(10) << "Descent" << setw(20) << "ADDR" << setw(10) << "From" << setw(5) << "Top" << setw(8) << "updated" << setw(8) << "changed"<<  endl;
		outfile << "==================================================================================================================================================" << endl;
		for(itr = this->table.begin() ; itr != this->table.end() ;itr++) {  						
			en = (*itr);
			outfile << setw(50) << en->getLongName() << setw(8) << en->getScope() << setw(7) << en->getTypeStr() << setw(20) << en->getVal() << setw(10) << en->getParent() << setw(10) << en << setw(10) << en->getDescentFrom() <<setw(20) << en->getAddr() << setw(10) << en->getFrom() << setw(5) << en->isTop() << setw(8) << en->isUpdated() << setw(8) << en->isChanged()<< endl;
		}
		outfile << "==================================================================================================================================================" << endl; 
		outfile << "Total entries in WOPM: " << this->table.size() << endl << endl;

		outfile.close();

		/* 輸出到螢幕上
		cout << setw(45) << " WOP Name" << setw(45) << "Value" << setw(10) << "Parent" << setw(10) << "Self" << setw(30) << "addr" << setw(5) << "Top" << endl;
		cout << "==================================================================================================================================================" << endl;
		for(itr = this->table.begin() ; itr != this->table.end() ;itr++) {  						
			en = (*itr);
			cout << setw(45) << en->getName() << setw(45) << en->getVal() << setw(10) << en->getParent() << setw(10) << en << setw(30) << en->getAddr() << setw(5) << en->isTop() << endl;
		}
		cout << "==================================================================================================================================================" << endl; 
		cout << "Total entries in WOPM: " << this->table.size() << endl << endl;
		*/
	}